package com.yeskery.nut.application;

import com.yeskery.nut.bean.*;
import com.yeskery.nut.bean.scheduling.ScheduledBeanPostProcessor;
import com.yeskery.nut.bean.scheduling.ScheduledExecutor;
import com.yeskery.nut.bind.CustomMethodParamBindHandler;
import com.yeskery.nut.bind.EmitterResponseBodyHandler;
import com.yeskery.nut.bind.HttpResponseBodyHandler;
import com.yeskery.nut.bind.ResponseBodyHandler;
import com.yeskery.nut.core.*;
import com.yeskery.nut.event.ApplicationEventMulticaster;
import com.yeskery.nut.event.ApplicationListener;
import com.yeskery.nut.event.EventListenerBeanPostProcessor;
import com.yeskery.nut.http.*;
import com.yeskery.nut.plugin.Plugin;
import com.yeskery.nut.plugin.PluginManager;
import com.yeskery.nut.script.expression.NutExpression;
import com.yeskery.nut.script.function.Function;
import com.yeskery.nut.transaction.DefaultTransactionManager;
import com.yeskery.nut.transaction.TransactionEventListenerBeanPostProcessor;
import com.yeskery.nut.transaction.TransactionManager;
import com.yeskery.nut.util.ReflectUtils;
import com.yeskery.nut.websocket.WebSocketBeanFactoryPriorityPostProcessor;
import com.yeskery.nut.websocket.WebSocketBeanPriorityPostProcessor;

import javax.sql.DataSource;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Nut自动配置处理器
 * @author sprout
 * @version 1.0
 * 2022-07-31 15:18
 */
public class NutAutoConfigureProcessRegister {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(NutAutoConfigureProcessRegister.class.getName());

    /** 内部自动配置bean名称前缀 */
    private static final String INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL = "$_";

    /** 事件广播器 */
    private final ApplicationEventMulticaster applicationEventMulticaster;

    /** bean注册器 */
    private final FactoryBeanRegister factoryBeanRegister;

    /** Nut Web 相关配置对象 */
    private final NutConfigure nutConfigure;

    /**
     * 构建Nut自动配置bean后置处理器
     * @param applicationEventMulticaster 事件广播器
     * @param factoryBeanRegister bean注册器
     * @param nutConfigure Nut Web 相关配置对象
     */
    public NutAutoConfigureProcessRegister(ApplicationEventMulticaster applicationEventMulticaster,
                                           FactoryBeanRegister factoryBeanRegister, NutConfigure nutConfigure) {
        this.applicationEventMulticaster = applicationEventMulticaster;
        this.factoryBeanRegister = factoryBeanRegister;
        this.nutConfigure = nutConfigure;
    }

    /**
     * 注册默认的自动配置BeanPostProcessor对象
     */
    public void registerDefaultAutoConfigureBeanPostProcessor() {
        // 注册WebSocket自动配置
        factoryBeanRegister.registerBean(ReflectUtils.getDefaultBeanName(WebSocketBeanPriorityPostProcessor.class),
                new WebSocketBeanPriorityPostProcessor(), BeanPriorityPostProcessor.class);

        // 注册事件监听器自动配置
        factoryBeanRegister.registerBean(ReflectUtils.getDefaultBeanName(EventListenerBeanPostProcessor.class),
                new EventListenerBeanPostProcessor(), BeanPostProcessor.class);

        // 注册事务事件监听器自动配置
        factoryBeanRegister.registerBean(ReflectUtils.getDefaultBeanName(TransactionEventListenerBeanPostProcessor.class),
                new TransactionEventListenerBeanPostProcessor(), BeanPostProcessor.class);

        // 注册定时调度自动配置
        factoryBeanRegister.registerBean(ReflectUtils.getDefaultBeanName(ScheduledBeanPostProcessor.class),
                new ScheduledBeanPostProcessor(((ApplicationContext) factoryBeanRegister).getBean(ScheduledExecutor.class)), BeanPostProcessor.class);
    }

    /**
     * 注册默认的自动配置BeanFactoryPostProcessor对象
     */
    public void registerDefaultAutoConfigureBeanFactoryPostProcessor() {
        // 注册应用事件监听器自动配置
        doRegisterApplicationEventAutoConfigureBeanFactoryPostProcessor();

        // 注册插件自动配置
        doRegisterCustomPluginAutoConfigureBeanFactoryPostProcessor();

        // 注册Nut Web应用配置自动配置
        doRegisterWebConfigurerAutoConfigureBeanFactoryPostProcessor();

        // 注册FactoryBean自动配置
        doRegisterFactoryBeanAutoConfigureBeanFactoryPostProcessor();

        // 注册自动探测器自动配置
        doRegisterCustomAutoDetectorAutoConfigureBeanFactoryPostProcessor();

        // 注册自定义方法参数绑定处理器自动配置
        doRegisterCustomMethodParamBindHandlerAutoConfigureBeanFactoryPostProcessor();

        // 注册响应体处理器自动配置
        doRegisterResponseBodyHandlerAutoConfigureBeanFactoryPostProcessor();

        // 注册Http响应体处理器自动配置
        doRegisterHttpResponseBodyHandlerAutoConfigureBeanFactoryPostProcessor();

        // 注册自定义脚本视图表达式自动配置
        doRegisterCustomScriptExpressionAutoConfigureBeanFactoryPostProcessor();

        // 注册脚本自定义函数自动配置
        doRegisterCustomScriptFunctionAutoConfigureBeanFactoryPostProcessor();

        // 注册命令行执行函数自动配置
        doRegisterCustomCommandLineRunnerAutoConfigureBeanFactoryPostProcessor();

        // 注册程序执行函数自动配置
        doRegisterCustomApplicationRunnerAutoConfigureBeanFactoryPostProcessor();

        // 设置事务管理器数据源自动配置
        doConfigTransactionManagerDataSourceAutoConfigureFactoryPostProcessor();

        // 设置WebSocket自动配置
        factoryBeanRegister.registerBean(ReflectUtils.getDefaultBeanName(WebSocketBeanFactoryPriorityPostProcessor.class),
                new WebSocketBeanFactoryPriorityPostProcessor(), BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册应用事件监听器自动配置
     */
    private void doRegisterApplicationEventAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "applicationEventAutoConfigureBeanFactoryPostProcessor",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
                    for (ApplicationListener<?> applicationListener : applicationContext.getBeans(ApplicationListener.class)) {
                        try {
                            applicationEventMulticaster.addApplicationListener(applicationListener);
                        } catch (Exception e) {
                            logger.logp(Level.SEVERE, ApplicationEventMulticaster.class.getName(), "addApplicationListener",
                                    "Auto Configure Process Fail, Type [" + ApplicationListener.class.getName()
                                            + "] Class [" + applicationListener.getClass().getName() + "].", e);
                        }
                    }
                }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册插件自动配置
     */
    private void doRegisterCustomPluginAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "customPluginAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
            for (Plugin plugin : applicationContext.getBeans(Plugin.class)) {
                try {
                    nutConfigure.getPluginManager().registerPlugin(plugin);
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, PluginManager.class.getName(), "registerPlugin",
                            "Auto Configure Process Fail, Type [" + Plugin.class.getName()
                                    + "] Class [" + plugin.getClass().getName() + "].", e);
                }
            }
        }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册Nut Web应用配置自动配置
     */
    private void doRegisterWebConfigurerAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "webConfigurerAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
                    for (WebConfigurer webConfigurer : applicationContext.getBeans(WebConfigurer.class)) {
                        try {
                            webConfigurer.configure(nutConfigure);
                        } catch (Exception e) {
                            logger.logp(Level.SEVERE, WebConfigurer.class.getName(), "configure",
                                    "Auto Configure Process Fail, Type [" + WebConfigurer.class.getName()
                                            + "] Class [" + webConfigurer.getClass().getName() + "].", e);
                        }
                    }
                }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册FactoryBean自动配置
     */
    private void doRegisterFactoryBeanAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "factoryBeanAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
                    for (FactoryBean factoryBean : applicationContext.getBeans(FactoryBean.class)) {
                        try {
                            ((FactoryBeanRegister) applicationContext).registerFactoryBean(
                                    ReflectUtils.getDefaultBeanName(factoryBean.getClass()), factoryBean);
                        } catch (Exception e) {
                            logger.logp(Level.SEVERE, FactoryBeanRegister.class.getName(), "registerFactoryBean",
                                    "Auto Configure Process Fail, Type [" + FactoryBeanRegister.class.getName()
                                            + "] Class [" + factoryBean.getClass().getName() + "].", e);
                        }
                    }
                }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册自动探测器自动配置
     */
    private void doRegisterCustomAutoDetectorAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "customAutoDetectorAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
            for (AutoDetector autoDetector : applicationContext.getBeans(AutoDetector.class)) {
                try {
                    nutConfigure.registerAutoDetector(autoDetector);
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, nutConfigure.getClass().getName(), "registerAutoDetector",
                            "Auto Configure Process Fail, Type [" + AutoDetector.class.getName()
                                    + "] Class [" + autoDetector.getClass().getName() + "].", e);
                }
            }
        }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册自定义方法参数绑定处理器自动配置
     */
    private void doRegisterCustomMethodParamBindHandlerAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "customMethodParamBindHandlerAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
            for (CustomMethodParamBindHandler<?> handler : applicationContext.getBeans(CustomMethodParamBindHandler.class)) {
                try {
                    nutConfigure.addMethodParamBindHandler(handler);
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, nutConfigure.getClass().getName(), "addMethodParamBindHandler",
                            "Auto Configure Process Fail, Type [" + CustomMethodParamBindHandler.class.getName()
                                    + "] Class [" + handler.getClass().getName() + "].", e);
                }
            }
        }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册响应体处理器自动配置
     */
    private void doRegisterResponseBodyHandlerAutoConfigureBeanFactoryPostProcessor() {
        nutConfigure.addResponseBodyHandler(new EmitterResponseBodyHandler());
        nutConfigure.addResponseBodyHandler(new SseEmitterResponseBodyHandler());
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "responseBodyHandlerAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
            for (ResponseBodyHandler handler : applicationContext.getBeans(ResponseBodyHandler.class)) {
                try {
                    nutConfigure.addResponseBodyHandler(handler);
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, nutConfigure.getClass().getName(), "addResponseBodyHandler",
                            "Auto Configure Process Fail, Type [" + ResponseBodyHandler.class.getName()
                                    + "] Class [" + handler.getClass().getName() + "].", e);
                }
            }
        }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册Http响应体处理器自动配置
     */
    private void doRegisterHttpResponseBodyHandlerAutoConfigureBeanFactoryPostProcessor() {
        nutConfigure.addHttpResponseBodyHandler(new ApplicationJsonHttpResponseBodyHandler());
        nutConfigure.addHttpResponseBodyHandler(new ApplicationXmlHttpResponseBodyHandler());
        nutConfigure.addHttpResponseBodyHandler(new TextPlainHttpResponseBodyHandler());
        nutConfigure.addHttpResponseBodyHandler(new ApplicationOctetStreamHttpResponseBodyHandler());
        nutConfigure.addHttpResponseBodyHandler(new ResponseEntityHttpResponseBodyHandler());
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "httpResponseBodyHandlerAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
                    for (HttpResponseBodyHandler handler : applicationContext.getBeans(HttpResponseBodyHandler.class)) {
                        try {
                            nutConfigure.addHttpResponseBodyHandler(handler);
                        } catch (Exception e) {
                            logger.logp(Level.SEVERE, nutConfigure.getClass().getName(), "addResponseBodyHandler",
                                    "Auto Configure Process Fail, Type [" + ResponseBodyHandler.class.getName()
                                            + "] Class [" + handler.getClass().getName() + "].", e);
                        }
                    }
                }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册自定义脚本视图表达式自动配置
     */
    private void doRegisterCustomScriptExpressionAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "customScriptExpressionAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
            for (NutExpression expression : applicationContext.getBeans(NutExpression.class)) {
                try {
                    nutConfigure.registerCustomScriptExpression(expression);
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, nutConfigure.getClass().getName(), "registerCustomScriptExpression",
                            "Auto Configure Process Fail, Type [" + NutExpression.class.getName()
                                    + "] Class [" + expression.getClass().getName() + "].", e);
                }
            }
        }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册脚本自定义函数自动配置
     */
    private void doRegisterCustomScriptFunctionAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "customScriptFunctionAutoConfigureBean",
                (BeanFactoryPriorityPostProcessor) applicationContext -> {
            for (Function function : applicationContext.getBeans(Function.class)) {
                try {
                    nutConfigure.registerCustomScriptFunction(function);
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, nutConfigure.getClass().getName(), "registerCustomScriptFunction",
                            "Auto Configure Process Fail, Type [" + Function.class.getName()
                                    + "] Class [" + function.getClass().getName() + "].", e);
                }
            }
        }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册命令行执行函数自动配置
     */
    private void doRegisterCustomCommandLineRunnerAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "customCommandLineRunnerAutoConfigureBean",
                new BeanFactoryPriorityPostProcessor() {
            @Override
            public void process(ApplicationContext applicationContext) {
                for (CommandLineRunner commandLineRunner : applicationContext.getBeans(CommandLineRunner.class)) {
                    try {
                        nutConfigure.addRunner(commandLineRunner);
                    } catch (Exception e) {
                        logger.logp(Level.SEVERE, nutConfigure.getClass().getName(), "addRunner",
                                "Auto Configure Process Fail, Type [" + CommandLineRunner.class.getName()
                                        + "] Class [" + commandLineRunner.getClass().getName() + "].", e);
                    }
                }
            }

            @Override
            public int getOrder() {
                return Order.MIN;
            }
        }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 注册程序执行函数自动配置
     */
    private void doRegisterCustomApplicationRunnerAutoConfigureBeanFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "customApplicationRunnerAutoConfigureBean",
                new BeanFactoryPriorityPostProcessor() {
            @Override
            public void process(ApplicationContext applicationContext) {
                for (ApplicationRunner applicationRunner : applicationContext.getBeans(ApplicationRunner.class)) {
                    try {
                        nutConfigure.addRunner(applicationRunner);
                    } catch (Exception e) {
                        logger.logp(Level.SEVERE, nutConfigure.getClass().getName(), "addRunner",
                                "Auto Configure Process Fail, Type [" + ApplicationRunner.class.getName()
                                        + "] Class [" + applicationRunner.getClass().getName() + "].", e);
                    }
                }
            }

            @Override
            public int getOrder() {
                return Order.MIN;
            }
        }, BeanFactoryPriorityPostProcessor.class);
    }

    /**
     * 设置事务管理器数据源自动配置
     */
    private void doConfigTransactionManagerDataSourceAutoConfigureFactoryPostProcessor() {
        factoryBeanRegister.registerBean(INNER_AUTO_CONFIGURE_BEAN_NAME_PREFIX_SYMBOL + "configTransactionManagerDataSourceAutoConfigure",
                new BeanFactoryPostProcessor() {
            @Override
            public void process(ApplicationContext applicationContext) {
                try {
                    DataSource dataSource = applicationContext.getBean(DataSource.class);
                    DefaultTransactionManager transactionManager = applicationContext.getBean(
                            TransactionManager.DEFAULT_TRANSACTION_MANAGER_NAME, DefaultTransactionManager.class);
                    transactionManager.setDataSource(dataSource);
                } catch (NoSuchBeanException e) {
                    logger.warning("TransactionManager Can Not Found Default DataSource.");
                }
            }

            @Override
            public int getOrder() {
                return Order.MAX;
            }
        }, BeanFactoryPostProcessor.class);
    }
}
